﻿// yy_prf_pipe.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// 将廓线数据线生成有粗度的管子mesh。

#include <iostream>
#include <string>
#include <vector>
#include "Vectors.h"
#include "Matrices.h"
#include "Plane.h"
#include "Line.h"
#include "Pipe.h"
#include "ajson5.h"
#include "wdirtools.h"

using namespace ArduinoJson;
using namespace std;

///////////////////////////////////////////////////////////////////////////////
// generate points of a circle on xy plane
///////////////////////////////////////////////////////////////////////////////
std::vector<Vector3> buildCircle(float radius, int steps)
{
    std::vector<Vector3> points;
    if (steps < 2) return points;

    const float PI2 = acos(-1) * 2.0f;
    float x, y, a;
    for (int i = 0; i <= steps; ++i)
    {
        a = PI2 / steps * i;
        x = radius * cosf(a);
        y = radius * sinf(a);
        points.push_back(Vector3(x, y, 0));
    }
    return points;
}


void processOneLine(JsonObject& feature, vector<Vector3>& allVertices, vector<unsigned int>& allIndices, int segCnt, float radius ) {
    int vsize0 = allVertices.size();
    int isize0 = allIndices.size();
    Pipe pipe;
    std::vector<Vector3> path ;
    std::vector<Vector3> contour = buildCircle( radius , segCnt ); // 这个轮廓线是xy平面的，z恒等于0即可。
    JsonObject& geometry = feature["geometry"].as<JsonObject>();
    JsonArray& coords = geometry["coordinates"].as<JsonArray>();
    if (coords.size() < 2) {
        return;//no lines.
    }
    //点太多webgl渲染不了，这里把每个线显示到 20 个点
    int pointStep = 1;
    int np = coords.size();
    while (np > 10) {
        np = np / 2;
        pointStep *= 2;
    }
    cout << "np from " << coords.size() << " to " << np << endl; 
    for (int iv = 0; iv < coords.size(); iv+= pointStep ) {
        JsonArray& xyzw = coords[iv].as<JsonArray>();
        double x = xyzw[0].as<double>();
        double y = xyzw[1].as<double>();
        double z = xyzw[2].as<double>();
        path.push_back(Vector3(x, y, z));
    }
    pipe.set(path, contour); // generate tube
    //cap bottom
    allVertices.push_back(path[0]);
    int capbottomCenterVertIndex = allVertices.size() - 1;
    for (int ic = 0; ic < contour.size()-1; ++ic) {
        allIndices.push_back(capbottomCenterVertIndex);
        allIndices.push_back(capbottomCenterVertIndex+ic+1);
        allIndices.push_back(capbottomCenterVertIndex+ic+2);
    }
    //cylindar
    int capTopVert0Index = 0;
    int ccnt = pipe.getContourCount();
    for (int i = 0; i < ccnt-1 ; ++i) {
        const std::vector<Vector3>& c0 = pipe.getContour(i);
        int contour0vert0 = allVertices.size();
        int contour1vert0 = contour0vert0 + c0.size();
        for (int ic = 0; ic < c0.size(); ++ic) {
            allVertices.push_back(c0[ic]);
        }
        const std::vector<Vector3>& c1 = pipe.getContour(i+1);
        if (i == ccnt - 2) {
            capTopVert0Index = allVertices.size();
            for (int ic = 0; ic < c1.size(); ++ic) {
                allVertices.push_back(c1[ic]);
            }
        }
        for (int ic = 0; ic < c0.size()-1 ; ++ ic ) {
            allIndices.push_back(contour0vert0 + ic);
            allIndices.push_back(contour1vert0 + ic + 1);
            allIndices.push_back(contour1vert0 + ic);

            allIndices.push_back(contour0vert0 + ic);
            allIndices.push_back(contour0vert0 + ic + 1);
            allIndices.push_back(contour1vert0 + ic + 1);
        }
    }
    // cap top
    int capTopCenterVertIndex = allVertices.size();
    allVertices.push_back(path[path.size() - 1]);
    for (int ic = 0; ic < contour.size() - 1; ++ic) {
        allIndices.push_back(capTopCenterVertIndex);
        allIndices.push_back(capTopVert0Index + ic + 1);
        allIndices.push_back(capTopVert0Index + ic + 0);
    }
    int lineVerts = allVertices.size() - vsize0;
    int lineIndices = allIndices.size() - isize0;
    int numTri = lineIndices / 3;
    cout << "line nv " << lineVerts << " ni " << lineIndices << " ntri " << numTri << endl; 
}


int main(int argc ,char* argv[])
{
    std::cout << "A program to generate pipe mesh from prf lines." << endl;
    cout << "v1.0 2025/7/28" << endl;
    cout << "usage: yy_prf_pipe.exe radius segcnt /dir/in.geojson /out/put.json" << endl;
    cout << "radius in meter, e.g. 20000" << endl;
    cout << "segcnt e.g. 6 9 12" << endl;
    if (argc != 5) {
        cout << "error, argc not 5. argc=" << argc << endl;
        return 11;
    }
    double radius = atof(argv[1]);// 10000.0;
    int contourSegmentCount = atof(argv[2]);// 6;
    string infile = argv[3];// "ionprf_20250704_0000_ecef.geojson";
    string outfile = argv[4];// "ionprf_20250704_0000_ecef_pipe.json";

    cout << "radius(m) " << radius << endl;
    cout << "segcnt " << contourSegmentCount << endl;
    cout << "infile " << infile << endl;
    cout << "outfile " << outfile << endl;

    string inText;
    bool jsonOk = wDirTools::readFullText(infile, inText);
    if (jsonOk == false) {
        cout << "bad input json file " << infile << endl;
        return 12;
    }

    DynamicJsonBuffer jsonBuffer;
    JsonObject& jsonRoot = jsonBuffer.parse(inText);
    JsonArray& jsonFeatures = jsonRoot["features"].as<JsonArray>();
    vector<Vector3> allVertices;
    vector<unsigned int> allIndices;
    

    for (int iline = 0; iline < jsonFeatures.size(); ++iline) {
        cout << "line " << iline << " " ;
        JsonObject& featureOne = jsonFeatures[iline].as<JsonObject>();
        processOneLine(featureOne, allVertices, allIndices , contourSegmentCount, radius);
    }

    JsonObject& outroot = jsonBuffer.createObject();
    outroot["nver"] = allVertices.size();
    outroot["nind"] = allIndices.size();
    outroot["ntri"] = allIndices.size()/3;

    cout << "--- total ---" << endl;
    cout << "nver " << allVertices.size() << " nind " << allIndices.size() << " ntri " << allIndices.size() / 3 << endl;

    JsonArray& verArr = outroot.createNestedArray("verts");
    JsonArray& indArr = outroot.createNestedArray("indis");
    for (int iv = 0; iv < allVertices.size(); ++iv) {
        JsonArray& xyz = verArr.createNestedArray();
        xyz.add( allVertices[iv].x );
        xyz.add(allVertices[iv].y);
        xyz.add(allVertices[iv].z);
    }
    for (int ii = 0; ii < allIndices.size(); ++ii) {
        indArr.add(allIndices[ii]);
    }
    
    string outText;
    outroot.printTo(outText);
    ofstream ofs(outfile.c_str());
    if (ofs.good()) {
        ofs << outText;
        ofs.close();
    }
    else {
        cout << "failed to create output file " << outfile << endl; 
        return 13;
    }
    cout << "done" << endl;
    return 0;
    // demo try
    //Pipe pipe;
    //std::vector<Vector3> path = { Vector3(0,0,0) , Vector3(1,0,0) };
    //std::vector<Vector3> contour = buildCircle( 1.0, 12 ) ; // 这个轮廓线是xy平面的，z恒等于0即可。
    //pipe.set(path, contour);
    //int ccnt = pipe.getContourCount();
    //for (int i = 0; i < ccnt; ++i) {
    //    cout << "contour " << i << endl;
    //    const std::vector<Vector3>& c = pipe.getContour(i);
    //    for (int j = 0; j < c.size(); ++j) {
    //        cout << "  " << c[j] << endl;
    //    }
    //}

}
